package action

import (
	"context"
	"strings"

	"github.com/glennliao/apijson-go/config"
	"github.com/glennliao/apijson-go/consts"
	"github.com/glennliao/apijson-go/model"
	"github.com/glennliao/apijson-go/query"
	"github.com/glennliao/apijson-go/util"
	"github.com/gogf/gf/v2/util/gconv"
)

// 操作 非get查询的request表中的请求
type C操作 struct {
	S上下文  context.Context
	Q请求配置 *config.Q请求配置
	F方法   string

	Q请求参数 model.Map

	C错误 error

	Z子节点  map[string]*J节点
	J节点映射 map[string]*J节点

	// 关闭访问权限验证, 默认否
	B不验证访问权限 bool
	// 关闭请求验证开关, 默认否
	B不验证请求 bool

	// 数据库字段命名风格 请求传递到数据库中
	S数据库字段风格 config.Z字段风格转换函数

	// 数据库返回的字段风格
	JSON字段风格 config.Z字段风格转换函数

	C操作配置 *config.C操作配置

	X新建查询 func(ctx context.Context, req model.Map) *query.C查询结构体
	X新建操作 func(ctx context.Context, method string, req model.Map) *C操作

	G钩子映射 map[string][]*G钩子

	F返回结果 model.Map
}

func X新建操作(ctx context.Context, C操作配置 *config.C操作配置, 方法 string, 请求参数 model.Map) *C操作 {

	请求配置, 错误 := J检查标签(请求参数, 方法, C操作配置)
	if 错误 != nil {
		return &C操作{
			C错误: 错误,
		}
	}

	delete(请求参数, consts.T标签)
	delete(请求参数, consts.B版本)

	操作实例 := &C操作{
		S上下文:  ctx,
		Q请求配置: 请求配置,
		F方法:   方法,
		Q请求参数: 请求参数,
		Z子节点:  map[string]*J节点{},
		J节点映射: map[string]*J节点{},
		C操作配置: C操作配置,
	}
	return 操作实例
}

func (操作实例 *C操作) J解析() error {

	if 操作实例.C错误 != nil {
		return 操作实例.C错误
	}

	结构定义 := 操作实例.Q请求配置.J结构定义

	for 键, 值 := range 操作实例.Q请求参数 {

		结构键 := 键
		if strings.HasSuffix(键, consts.L列表键后缀) {
			结构键 = util.Y移除后缀(键, consts.L列表键后缀)
		}

		结构, 存在 := 结构定义[键]
		if !存在 {
			if 结构, 存在 = 结构定义[结构键]; !存在 { // User[]可读取User或者User[]
				return consts.X新建结构键未找到错误(键)
			}
		}

		var 列表 []model.Map
		_值, 是Map := 值.(model.Map)
		if 是Map { // 将所有node都假设成列表, 如果单个则看成一个元素的批量
			列表 = []model.Map{_值}
		} else {
			for _, 映射 := range gconv.Maps(值) {
				列表 = append(列表, 映射)
			}
		}

		节点 := X新建节点(键, 列表, 结构, 操作实例.Q请求配置.Z执行器映射[键])
		节点.S上下文 = 操作实例.S上下文
		节点.S所属操作 = 操作实例
		操作实例.J节点映射[键] = &节点
		节点.J节点映射 = 操作实例.J节点映射
		错误 := 节点.J解析(操作实例.S上下文, 操作实例.F方法)
		if 错误 != nil {
			return 错误
		}

		操作实例.Z子节点[键] = &节点
	}

	return nil
}

func (操作实例 *C操作) Z执行钩子(索引 int, 在事务中 bool) error {
	键 := 操作实例.Q请求配置.Z执行队列[索引]
	节点 := 操作实例.Z子节点[键]
	节点钩子请求 := &G钩子请求{
		J节点:     节点,
		Q请求方法:   操作实例.F方法,
		S上下文:    操作实例.S上下文,
		X下一个索引:  -1,
		S是否在事务中: 在事务中,
		G钩子列表:   G根据访问名获取钩子列表(操作实例.G钩子映射, 键),
	}

	节点钩子请求.W完成处理程序 = func(ctx context.Context, 节点 *J节点, 方法 string) error {

		if 索引+1 < len(操作实例.Q请求配置.Z执行队列) {
			return 操作实例.Z执行钩子(索引+1, 在事务中)
		}

		// 执行完了普通hook的before,开始执行事务内
		if !在事务中 {

			事务处理器 := W无事务处理函数

			if 操作实例.Q请求配置.S事务开关 != nil && *操作实例.Q请求配置.S事务开关 == true {
				处理器 := H获取事务处理函数(操作实例.S上下文, 操作实例)
				if 处理器 == nil {
					错误 := consts.X新建系统错误("transaction handler is nil")
					return 错误
				}
				事务处理器 = 处理器
			}

			错误 := 事务处理器(操作实例.S上下文, func(ctx context.Context) error {
				return 操作实例.Z执行钩子(0, !在事务中)
			})

			return 错误
		}

		var 错误 error
		操作实例.F返回结果[键], 错误 = 节点.Z执行(操作实例.S上下文, 操作实例.F方法)
		return 错误
	}

	错误 := 节点钩子请求.X下一个()
	return 错误
}

func (操作实例 *C操作) J结果() (model.Map, error) {

	错误 := 操作实例.J解析()
	if 错误 != nil {
		return nil, 错误
	}

	操作实例.F返回结果 = model.Map{}

	错误 = 操作实例.Z执行钩子(0, false)
	if 错误 != nil {
		操作实例.C错误 = 错误
	}
	return 操作实例.F返回结果, 错误
}

func J检查标签(请求参数 model.Map, 方法 string, 请求配置 *config.C操作配置) (*config.Q请求配置, error) {
	_标签, 存在 := 请求参数[consts.T标签]
	if !存在 {
		return nil, consts.C错误_无标签
	}

	标签 := gconv.String(_标签)
	版本 := 请求参数[consts.B版本]

	请求配置实例, 错误 := 请求配置.H获取请求配置(标签, 方法, gconv.String(版本))
	if 错误 != nil {
		return nil, 错误
	}

	return 请求配置实例, nil
}
